﻿using System.Data.SqlClient;
using OperationPlugins.ExtensionMethods;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using TimeProject.Engine.DbModels;

namespace TimeProject.Engine.DbContent
{
    public class TimeProjectDbContext : DbContext
    {

        public DbSet<User> Users { get; set; }
        public DbSet<UserActivity> UserActivities { get; set; }
        public DbSet<UserOption> UserOptions { get; set; }
        public DbSet<Organization> Organizations { get; set; }
        public DbSet<OrganizationUser> OrganizationUsers { get; set; }

        /// <summary>
        /// Initializes a new instance of the TimeProjectDbContext class.
        /// </summary>
        /// <param name="nameOrConnectionString">The database connection string or name of the connection string.</param>
        /// <param name="getTimestampData">GetTimestampDataDelegate</param>
        public TimeProjectDbContext(string nameOrConnectionString, GetTimestampDataDelegate getTimestampData)
            : base(nameOrConnectionString)
        {
            GetTimestampData = getTimestampData;
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
        }

        public TAdHoc[] Query<TAdHoc>(TAdHoc query) where TAdHoc : AdHocBase
        {
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }

            TAdHoc[] items = Database.SqlQuery<TAdHoc>(query.GetSql(), new List<object>(query.GetParameters()).ToArray()).ToArray();
            return items;
        }

        /// <summary>
        /// Saves all changes made in this context to the underlying database.
        /// </summary>
        /// <returns>The number of objects written to the underlying database.</returns>
        public override int SaveChanges()
        {
            TimestampData timestampData = GetTimestampData();

            List<DbEntityEntry> entries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added || 
                                                                             x.State == EntityState.Modified).ToList();

            foreach (DbEntityEntry entry in entries)
            {
                EntityState state = entry.State;

                EntityBase entity = entry.Entity as EntityBase;
                
                if (entity == null)
                {
                    throw new InvalidOperationException("EntityBase was expected.");
                }

                if (state == EntityState.Added)
                {
                    entity.Creator = timestampData.RequestUsername;
                    entity.Created = timestampData.RequestTimestamp;
                }
                else if (state == EntityState.Modified)
                {
                    if (entity.IsActive)
                    {
                        entity.Modifier = timestampData.RequestUsername;
                        entity.Modified = timestampData.RequestTimestamp;
                    }
                    else
                    {
                        entity.Passivator = timestampData.RequestUsername;
                        entity.Passivated = timestampData.RequestTimestamp;
                    }
                }
            }

            return base.SaveChanges();
        }

        public GetTimestampDataDelegate GetTimestampData { set; get; }

        public delegate TimestampData GetTimestampDataDelegate();

        public struct TimestampData
        {
            public string RequestUsername { get; set; }
            public DateTime RequestTimestamp { get; set; }
        }
    }
}
